package com.example.amazing_wangjj.config;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;



import java.util.Arrays;
import java.util.List;
/**
 * 简单声明一下ContentRetriever类的作用
 * ContentRetriever 实际承担：
 * - 内容检索：通过 EmbeddingStore 执行向量相似度搜索
 * - 结果过滤：通过 minScore 过滤低相关性内容
 * - 结果排序：按相似度得分降序排列
 * - 结果裁剪：通过 maxResults 控制返回数量
 *
 *
 * 懂了，大概意思实在这个框架下，所有的请求都会优先进行RAG检索，
 * 然后将筛选，蒸馏出的知识库数据全部作为“提示词”提交给大模型，然后大模型才会生成回答
 */

import static org.apache.poi.hslf.record.RecordTypes.List;

@Configuration
//自定义配置类，注册一个Rag实现(读取并上传文档，并暴露能够查询向量数据库的方法)，在@AiService中使用，方法名对应注解中的参数值
public class RAGConfig {

    /**
     * 最终版：导入 向量数据库的存储对象，通用文本解析模型
     *
     * @return
     */
    @Autowired
    private EmbeddingStore<TextSegment> embeddingStore;
    @Autowired
    private EmbeddingModel embeddingModel;

    @Bean
        //简单的RAG实现，读取并上传文档，使用默认的文档分割器，和默认的向量存储器InMemoryEmbeddingStore，调用ingest方法将文档内容分割成文本片段，并计算每个文本片段的嵌入向量，
    ContentRetriever contentRetriever() {
        //使用FileSystemDocumentLoader读取指定目录下的知识库文档
//并使用默认的文档解析器对文档进行解析
        Document document1 = FileSystemDocumentLoader.loadDocument("C:/Users/wangjj/Desktop/knowledge/医院信息.md", new TextDocumentParser());
        Document document2 = FileSystemDocumentLoader.loadDocument("C:/Users/wangjj/Desktop/knowledge/科室信息.md", new TextDocumentParser());
        Document document3 = FileSystemDocumentLoader.loadDocument("C:/Users/wangjj/Desktop/knowledge/神经内科.md", new TextDocumentParser());
        java.util.List<Document> documents = Arrays.asList(document1, document2, document3);

        //使用内存向量存储
        InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
        //底层使用默认的文档分割器，ingest方法将文档内容分割成文本片段，并将向量存入默认向量存储器，
        EmbeddingStoreIngestor.ingest(documents, embeddingStore);
        //提供给外界一个 允许从 嵌入存储（默认的存储库）里检索和查询内容相关的信息
        return EmbeddingStoreContentRetriever.from(embeddingStore);

    }

    /**
     * 高级的RAG实现，导入 向量数据库的存储对象，通用文本解析模型
     * 实现文档的解析上传，并暴露能够查询向量数据库的方法，不能写在这里，这样的化项目每次启动都会重新上传重复文档，向量数据库中文档过多
     *
     * @return
     */
    @Bean
    ContentRetriever contentRetrieverfinally() {
//        //使用FileSystemDocumentLoader读取指定目录下的知识库文档
//        //并使用默认的文档解析器对文档进行解析
//        Document document1 = FileSystemDocumentLoader.loadDocument("C:/Users/wangjj/Desktop/knowledge/医院信息.md", new TextDocumentParser());
//        Document document2 = FileSystemDocumentLoader.loadDocument("C:/Users/wangjj/Desktop/knowledge/科室信息.md", new TextDocumentParser());
//        Document document3 = FileSystemDocumentLoader.loadDocument("C:/Users/wangjj/Desktop/knowledge/神经内科.md", new TextDocumentParser());
//        java.util.List<Document> documents = Arrays.asList(document1, document2, document3);
//
//        //文本向量化并存入向量数据库：将每个片段进行向量化，执行ingest方法，将向量存入向量数据库Pinecone
//        EmbeddingStoreIngestor
//                .builder()
//                .embeddingStore(embeddingStore)  //这是注入的 向量数据库存储对象（基于Pinecone的）
//                .embeddingModel(embeddingModel)  //这是注入的 阿里的通用文本向量模型（用于分割，并将文本转化为向量）
//                .build()
//                .ingest(documents);  //执行
//
//        // 在 ingest 操作后添加验证日志
//        System.out.println("知识库已成功存储到 Pinecone，共处理文档数：" + documents.size());
//        System.out.println("使用的向量维度：" + embeddingModel.dimension());


        // 创建一个 EmbeddingStoreContentRetriever 对象，用于从数据库存储中检索内容，这个方法暴露到外部
        return EmbeddingStoreContentRetriever
                .builder()
                // 设置用于生成嵌入向量的嵌入模型
                .embeddingModel(embeddingModel)
                // 指定要使用的嵌入存储
                .embeddingStore(embeddingStore)
                // 设置最大检索结果数量，这里表示最多返回 1 条匹配结果
                .maxResults(1)
                // 设置最小得分阈值，只有得分大于等于 0.8 的结果才会被返回
                .minScore(0.8)
                // 构建最终的 EmbeddingStoreContentRetriever 实例
                .build();
    }

}
